/**
 *  Copyright (C) 2013  Piotr Szczepa�ski
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *   GNU General Public License for more details.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
package edu.pw.elka.gtsna.test_performance;

import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;

import edu.pw.elka.gtsna.centrality.betweenness.ProbabilisticSVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SVNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SVWeightedNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.SuccessiveGroupNodeBetweenness;
import edu.pw.elka.gtsna.centrality.betweenness.WeightedProbabilisticSV;
import edu.pw.elka.gtsna.centrality.betweenness.WeightedSuccessiveGroupNodeBetweenness;
import edu.pw.elka.gtsna.graph.Graph;
import edu.pw.elka.gtsna.graph.WeightedUndirectedGraph;
import edu.pw.elka.gtsna.graph_algorithms.FindComponents;
import edu.pw.elka.gtsna.graph_creator.TGFReader;
import edu.pw.elka.gtsna.graph_creator.WGMLReader;
import edu.pw.elka.gtsna.graph_creator.WTGFReader;
import edu.pw.elka.gtsna.montecarlo.MonteCarloShapleyValue;
import edu.pw.elka.gtsna.ranking.Ranking;
import edu.pw.elka.gtsna.utilities.Agregator;
import edu.pw.elka.gtsna.utilities.MathPowers;
import edu.pw.elka.gtsna.utilities.Timer;

/**
 * @author Piotr Lech Szczepa�ski
 * @author P.Szczepanski@ii.pw.edu.pl 
 *
 */
public class WSVBPerformerRealData extends Performer {
	// exact time
		Agregator[] MCTimes = new Agregator[2001];

		
		int numberOfMC = 1;

		int maxIterations = 10000;
		int MCstep=10;
		
		//final Graph gr = new UndirectedGraph(new TGFReader("ego-facebook.tar"));
		//final Graph gr =  new UndirectedGraph(new ScaleFreeGraphGenerator(150,(int)Math.sqrt(50)));
		//MathPowers.precompute(gr.getNumberOfNodes());
		
		
	    Graph[] gr = null;
		
		Map<Integer, Double> partialSolution = new HashMap<Integer, Double>();
	    long partiaInterations = 0;
	    long totalTime = 0;
		
	    WSVBPerformerRealData(){
		}
		
		/**
		 * @param threads
		 */
	    WSVBPerformerRealData(int threads) {
			super(threads);
			gr = new Graph[threads];
		
			for (int i= 0; i<threads; i++){
				gr[i] = new WeightedUndirectedGraph(new WGMLReader("celegansneural.gml")); 
			}
			
			FindComponents fc = new FindComponents(gr[0]);fc.compute();
			System.out.println(fc.getComponents());
			System.out.println(fc.getComponents().size());
			for (int a: gr[0].getAllNodes()){
				partialSolution.put(a, 0.0);
			}
		}

		synchronized static int index(double error){
			return (int) Math.min(Math.round(error*100),2000);
		}
		
		// MC_STEP, MC_ITERATIONS, NUMBER_OF_THREADS
		public static void main(String[] args) throws InterruptedException { 
			if (args.length < 3) {
				System.out.println("Missing arguments: "+"MC_STEP, MC_ITERATIONS, NUMBER_OF_THREADS");
				return;
			}

			
			WSVBPerformerRealData performer = new WSVBPerformerRealData(Integer.parseInt(args[2]));
			performer.setMCstep(Integer.parseInt(args[0]));
			performer.setMaxIterations(Integer.parseInt(args[1]));
			performer.runTest();
		}

		
		public void runTest() throws InterruptedException{
			System.out.println("running");

			System.out.println(gr[0].getAllNodes());
			System.out.println(gr[0].getNumOfEdges());
			
			for (int i=0; i<2001; i++){
				MCTimes[i]= new Agregator();
			}
			

			final Timer exactT = new Timer();	
			final Timer[] mcT = new Timer[NUMBER_OF_THREADS];
			
			MathPowers.precompute(gr[0].getNumOfNodes());
			
			final SVWeightedNodeBetweenness SV = new SVWeightedNodeBetweenness(gr[0]);
			exactT.start();
			SV.computeCentrality();
			exactT.stop();
			System.out.println("SV Exact: "+sixDForm.format(exactT.getMilliTime()));
			exactT.reset();

			Ranking<Integer> r = new Ranking(SV.getCentralities());
			r.printRanking();
			
			WeightedProbabilisticSV PSV = new WeightedProbabilisticSV(gr[0]);
			exactT.start();
			PSV.computeCentrality();
			exactT.stop();
			System.out.println("PSV Exact: "+sixDForm.format(exactT.getMilliTime()));
			exactT.reset();

			
			final WeightedSuccessiveGroupNodeBetweenness[] gS = new WeightedSuccessiveGroupNodeBetweenness[NUMBER_OF_THREADS];
			final Set<Integer> synset = new HashSet<Integer>();
			
			final Set<Integer> queue = Collections.synchronizedSet(synset);
			
			for (int i= 0; i<NUMBER_OF_THREADS; i++){
				final int  j = i;
				mcT[j] = new Timer();
				gS[j] = new WeightedSuccessiveGroupNodeBetweenness(gr[j]);
				executorService.execute(new Runnable() {
				    public void run() {
						gS[j].preProcess();
						System.out.println("Policzylo: "+j);
				    }
				});
				queue.add(j);
			}
			
			executorService.shutdown();
			
			try {
				executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS);
			} catch (InterruptedException e) {
				System.out.println("ERROR: PROCESS INTERUPTED");
			}
		
			executorService = Executors.newFixedThreadPool(Performer.NUMBER_OF_THREADS);
			
		    final Semaphore available = new Semaphore(NUMBER_OF_THREADS);
			
			for (int iter=0; iter<maxIterations; iter+=MCstep){
				available.acquire();

				executorService.execute(new Runnable() {
				    public void run() {
				    	int  j = getFree(queue);
				    	//System.out.println("j TO "+j);
				    	MonteCarloShapleyValue MC = new MonteCarloShapleyValue(gS[j], MCstep);

	    				mcT[j].start();
	    				MC.computeSolution(MCstep);
	    				mcT[j].stop();
	    				
	    				updateData(MC.getSolution(),MCstep,mcT[j].getMilliTime(),j);

	    				
			    		mcT[j].reset();
			    		   
						//MCTimes[index(Ranking.relativeError(partialSolution(), SV.getCentralities()))].observe(getTotalTime());
						addFree(queue,j);
						synchronized(this) {
							System.out.println(index(Ranking.relativeError(partialSolution(j), SV.getCentralities()))+" "+sixDForm.format(getTotalTime()));
						}
						available.release();
				    }
				});

		    }
			executorService.shutdown();
			
			try {
					executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
				} catch (InterruptedException e) {
					System.out.println("ERROR: PROCESS INTERUPTED");
				}

		}

		synchronized int getFree(Set<Integer> queue){
			int p = -1;
			for(Integer i : queue)
			{
			   p = i;
			   break;
			}
			queue.remove(p);
			return p;
		}
		
		synchronized void addFree(Set<Integer> queue, int j){
			queue.add(j);
		}
		
		
		synchronized void updateData(Map<Integer, Double> solution, int iter, long time,int j){
			rewriteCons(solution,j);
			increaseIterations(MCstep);
			increaseTime(time);
		}
		
		synchronized void rewriteCons(Map<Integer, Double> solution, int j){
			for (int a: gr[j].getAllNodes()){
				partialSolution.put(a, partialSolution.get(a)+solution.get(a));
			}	
		}

		synchronized void increaseIterations(int iter){
			partiaInterations+=iter;
		}

		synchronized void increaseTime(long time){
			totalTime+=time;
		}
		
		synchronized long getTotalTime(){
			return totalTime;
		}
		
		synchronized public Map<Integer, Double> partialSolution(int j){

			Map<Integer, Double> partialSV = new LinkedHashMap<Integer, Double>();
			for (int a: gr[j].getAllNodes()){
				if (partiaInterations!=0){
					partialSV.put(a, partialSolution.get(a)/(double)partiaInterations);
				} else {
					partialSV.put(a, 0.0);
				}
			}
			return partialSV;
		}
		
		
		public int getMaxIterations() {
			return maxIterations;
		}

		public void setMaxIterations(int maxIterations) {
			this.maxIterations = maxIterations;
		}

		public int getMCstep() {
			return MCstep;
		}

		public void setMCstep(int mCstep) {
			MCstep = mCstep;
		}
		
}
